#include "session_description.h"

#include <rtc_base/logging.h>
#include <sstream>

namespace xrtc {

const char k_media_protocol_dtls_savpf[] = "UDP/TLS/RTP/SAVPF";
const char k_meida_protocol_savpf[] = "RTP/SAVPF";	

static std::string connection_role_to_string(ConnectionRole role) {
    switch (role) {
        case ConnectionRole::ACTIVE:
            return "active";
        case ConnectionRole::PASSIVE:
            return "passive";
        case ConnectionRole::ACTPASS:
            return "actpass";
        case ConnectionRole::HOLDCONN:
            return "holdconn";
        default:
            return "none";
    }
}

static void build_candidates(std::shared_ptr<MediaContentDescription> content, std::stringstream& ss)
{
    for (auto c : content->get_candidates()) {
        ss << "a=candidate:" << c.m_foundation
           << " " << c.m_component
           << " " << c.m_protocol
           << " " << c.m_priority
           << " " << c.m_address.HostAsURIString()
           << " " << c.m_port
           << " typ " << c.m_type
           << "\r\n";
    }
}

bool ContentGroup::hasContentName(const std::string& content_name) {
    for (auto name : m_content_names) {
        if (name == content_name) {
            return true;
        }
    }

    return false;
}

void ContentGroup::addContentName(const std::string& content_name) {
    if (!hasContentName(content_name)) {
        m_content_names.push_back(content_name);
    }
}

void SessionDescription::addContent(std::shared_ptr<MediaContentDescription> content) {
	m_contents.push_back(content);
}

std::shared_ptr<MediaContentDescription> SessionDescription::get_content(const std::string &mid)
{
    for (const auto& content : m_contents) {
		if (content->mid() == mid) {
			return content;
		}
	}

	return nullptr;
}

void SessionDescription::addGroup(const ContentGroup &group) {
	m_content_groups.push_back(group);
}

std::vector<const ContentGroup *> SessionDescription::getGroupByName(const std::string &name) const {
    std::vector<const ContentGroup*> content_groups;
    for (const ContentGroup& group : m_content_groups) {
        if (group.getSemantics() == name) {
            content_groups.push_back(&group);
        }
    }

    return content_groups;
}

// 添加 ICE 传输信息，生成指纹校验
bool SessionDescription::addTransportInfo(const std::string &mid, const IceParameters &ice_param, 
										  rtc::RTCCertificate* certificate) {
	auto tdesc = std::make_shared<TransportDescription>();
	tdesc->m_mid = mid;
	tdesc->m_ICE_ufrag = ice_param.m_ice_ufrag;
	tdesc->m_ICE_pwd = ice_param.m_ice_pwd;

	if (certificate) {
		tdesc->m_identity_fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*certificate);
		if (!tdesc->m_identity_fingerprint) {
			RTC_LOG(LS_WARNING) << "get fingerprint failed";
            return false;
		}
	}

	if (m_sdp_type == SdpType::k_offer) {
		tdesc->m_connection_role = ConnectionRole::ACTPASS;
	} else {
		tdesc->m_connection_role = ConnectionRole::ACTIVE;
	}


	m_transport_infos.push_back(tdesc);
	return true;
}

void SessionDescription::add_transport_info(std::shared_ptr<TransportDescription> td) {
	m_transport_infos.push_back(td);
}

// 获取ICE 传输信息
std::shared_ptr<TransportDescription> SessionDescription::getTransportInfo(const std::string &mid) {
	for (auto tdesc : m_transport_infos) {
		if (tdesc->m_mid == mid) {
			return tdesc;
		}
	}

    return nullptr;
}

bool SessionDescription::is_bundle(const std::string &mid) {
	auto content_group = getGroupByName("BUNDLE");
	if (content_group.empty()) {
		return false;
	}

	for (auto group : content_group) {
		for (auto name : group->getContentNames()) {
			if (name == mid) {
				return true;
			}
		}
	}

    return false;
}

std::string SessionDescription::get_first_bundle_mid() {
	auto content_group = getGroupByName("BUNDLE");
	if (content_group.empty()) {
		return "";
	}

    return content_group[0]->getContentNames()[0];
}

// 添加Rtcp相关的参数
static void addRtcpFbLine(std::shared_ptr<CodecInfo> codec, std::stringstream& ss) {
	for (auto param : codec->m_feedback_params) {
		ss << "a=rtcp-fb:" << codec->m_id << " " << param.getId(); 
		if (!param.getParam().empty()) {
			ss << " " << param.getParam();
		}
		ss << "\r\n";
	}
}

// fmtp codec相关的参数
void addFmtpLine(std::shared_ptr<CodecInfo> codec, std::stringstream& ss) {
	if (!codec->m_codec_param.empty()) {
        ss << "a=fmtp:" << codec->m_id << " ";
        std::string data;
        for (auto param : codec->m_codec_param) {
            data += (";" + param.first + "=" + param.second);
        }
        // 去除第一个;
        data = data.substr(1);
        ss << data << "\r\n";
    }
}

// rtpmap
static void buildRtpMap(std::shared_ptr<MediaContentDescription> content, std::stringstream& ss) {
	for (auto codec : content->getCodecs()) {
		ss << "a=rtpmap:" << codec->m_id << " " << codec->m_name << "/" << codec->m_clockrate;
		if (MediaType::MEDIA_TYPE_AUDIO == content->type()) {
			auto audio_codec = std::dynamic_pointer_cast<AudioCodecInfo>(codec);
			if (audio_codec) {
				ss << "/" << audio_codec->m_channels;
			}	
		}

		ss << "\r\n";

		addRtcpFbLine(codec, ss);
		addFmtpLine(codec, ss);
	}
}

static void buildRtpDirection(std::shared_ptr<MediaContentDescription> content, std::stringstream& ss) {
	switch (content->getDirection()) {
		case RtpDirection::k_send_recv :
			ss << "a=sendrecv\r\n";
            break;
        case RtpDirection::k_send_only:
            ss << "a=sendonly\r\n";
            break;
        case RtpDirection::k_recv_only:
            ss << "a=recvonly\r\n";
            break;
        default:
            ss << "a=inactive\r\n";
            break;
	}
}

// 初始化音频编解码信息
AudioContentDescription::AudioContentDescription() {
	auto codec = std::make_shared<AudioCodecInfo>();
	codec->m_id = 111;
	codec->m_name = "opus";
	codec->m_clockrate = 48000; // 采样率
	codec->m_channels = 2; 		// 声道数

	// add feedback param
    codec->m_feedback_params.push_back(FeedbackParam("transport-cc"));

	 // add codec param
    codec->m_codec_param["minptime"] = "10";
    codec->m_codec_param["useinbandfec"] = "1";

	m_codecs.push_back(codec);
}

// 初始化视频编解码信息
VideoContentDescription::VideoContentDescription() {
	auto codec = std::make_shared<VideoCodecInfo>();
	codec->m_id = 107;
	codec->m_name = "H264";
	codec->m_clockrate = 90000; 
	m_codecs.push_back(codec);

	// add feedback param
    codec->m_feedback_params.push_back(FeedbackParam("goog-remb"));
    codec->m_feedback_params.push_back(FeedbackParam("transport-cc"));
    codec->m_feedback_params.push_back(FeedbackParam("ccm", "fir"));
    codec->m_feedback_params.push_back(FeedbackParam("nack"));
    codec->m_feedback_params.push_back(FeedbackParam("nack", "pli"));

	// add codec param
    codec->m_codec_param["level-asymmetry-allowed"] = "1";
    codec->m_codec_param["packetization-mode"] = "1";
    codec->m_codec_param["profile-level-id"] = "42e01f";

	// 用于WebRtc对视频数据包进行重传
	auto rtx_codec = std::make_shared<VideoCodecInfo>();
	rtx_codec->m_id = 99;
	rtx_codec->m_name = "rtx";
	rtx_codec->m_clockrate = 90000;

	// add codec param
    rtx_codec->m_codec_param["apt"] = std::to_string(codec->m_id);

	m_codecs.push_back(rtx_codec);
}

std::string SessionDescription::toString() {
    std::stringstream ss;
    // version
    ss << "v=0\r\n";
    // session origin
	// RFC 4566
	// o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
	ss << "o=- 0 2 IN IP4 127.0.0.1\r\n";
	// session name
	ss << "s=-\r\n";
	// time description
	ss << "t=0 0\r\n";

	// BUNDLE
    std::vector<const ContentGroup*> content_group = getGroupByName("BUNDLE");
    if (!content_group.empty()) {
        ss << "a=group:BUNDLE";
        for (auto group : content_group) {
            for (auto content_name : group->getContentNames()) {
                ss << " " << content_name;
            }
        }
        ss << "\r\n";
    }

	ss << "a=msid-semantic: WMS\r\n";

	for (auto content : m_contents) {
		// RFC 4566
        // m=<media> <port> <proto> <fmt>
		std::string fmt;
		for (auto codec : content->getCodecs()) {
			fmt.append(" ");
			fmt.append(std::to_string(codec->m_id));
		}

		ss << "m=" << content->mid() << " 9 " << k_media_protocol_dtls_savpf
            << fmt << "\r\n";

        ss << "c=IN IP4 0.0.0.0\r\n";
        ss << "a=rtcp:9 IN IP4 0.0.0.0\r\n";

		build_candidates(content, ss);

		// 向 offer 中添加 ice-ufrag 和 ice-pwd
		auto transport_info = getTransportInfo(content->mid());
		if (transport_info) {
			ss << "a=ice-ufrag:" << transport_info->m_ICE_ufrag << "\r\n";
            ss << "a=ice-pwd:" << transport_info->m_ICE_pwd << "\r\n";

			//  向 offer 中添加 fingerprint  
			auto fp = transport_info->m_identity_fingerprint.get();
            if (fp) {
                ss << "a=fingerprint:" << fp->algorithm << " " << fp->GetRfc4572Fingerprint()
                    << "\r\n";

				// 添加setup 信息
				ss << "a=setup:" << connection_role_to_string(transport_info->m_connection_role) << "\r\n";					
            }
		}

		ss << "a=mid:" << content->mid() << "\r\n";

		//  向 offer 中添加 rtcp-mux 以及传输方向
		buildRtpDirection(content, ss);
		if (content->getRtcpMux()) {
			ss << "a=rtcp-mux\r\n";
		}


		buildRtpMap(content, ss);
	}
	
    return ss.str();
}

} // namespace xrtc
